﻿
using IniParser;
using IniParser.Model;
using NationalInstruments.TestStand.Interop.API;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Windows.Forms;

namespace CLVisionModbusTS
{
    public class ModbusCallerTS
    {
        static object communNCManager;
        static ModbusOptions modbusOptions;
        static bool ModbusState = false;

        //读取TS参数，判断当前线程数量
        static int threadCount;
        //存储各个线程的Modbus通讯对象
        static Dictionary<int, Thread_Modbus> thread_Modbus_Dic = new Dictionary<int, Thread_Modbus>();
        //根节点对象。作用类似TreeView控件
        static CustomerNode baseCustomerNode;
        public ModbusCallerTS()
        {
        }

        /// <summary>
        /// 加载配置数据到TS
        /// </summary>
        /// <param name="strucVar_customer">TS节点</param>
        /// <param name="congigFilePath">自定义配置参数所在的路径</param>
        /// <returns></returns>
        private static bool LoadObjectToTS(PropertyObject strucVar_customer, string congigFilePath, out int ErrorCode, out string ErrorText)
        {
            try
            {
                ErrorCode = 0;
                ErrorText = "";
                modbusOptions = new ModbusOptions();
                //加载和PLC定义的固定数据
                modbusOptions.LoadConfig();

                //读取自定义参数
                string str = File.ReadAllText(congigFilePath);
                JObject jb_Customer = JsonConvert.DeserializeObject(str) as JObject;

                //创建base节点
                baseCustomerNode = new CustomerNode("base");
                CreateBaseNode(jb_Customer, "", ref baseCustomerNode);

                //获取当前数组长度，判断是否和线程数一致
                int arrayLength = strucVar_customer.GetNumElements();
                if (arrayLength < threadCount)
                {
                    strucVar_customer.InsertElements(arrayLength, threadCount - arrayLength);
                    for (int i = arrayLength; i < threadCount; i++)
                    {
                        //获取数组元素
                        PropertyObject propertyElement = strucVar_customer.GetPropertyObjectByOffset(i, 0);
                        //设置名称
                        propertyElement.Name = "Socket" + i.ToString();
                        //将本地参数写入TS
                        WriteDataToTS(jb_Customer, propertyElement, "");
                    }
                }
                else if (arrayLength > threadCount)
                {
                    strucVar_customer.DeleteElements(threadCount, arrayLength - threadCount);
                }
                else if (arrayLength == threadCount)
                {

                }
                return true;

            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = ex.Message + ex.StackTrace;
                throw;
            }
        }

        private static void WriteDataToTS(JObject jb, PropertyObject strucVar, string parentName)
        {
            //判断是不是数组，有多个元素
            bool isArray = IsArray(jb, out int length, out PropertyValueTypes propertyValueTypes);

            if (isArray)
            {
                //父节点
                PropertyObject parentPropertyObject = strucVar.Parent;
                //删除子节点
                parentPropertyObject.DeleteSubProperty(parentName, 0);
                //重新添加数组类型的子节点
                parentPropertyObject.NewSubProperty(parentName, propertyValueTypes, true, "", 0);
                PropertyObject currentStrucVar_str = parentPropertyObject.GetPropertyObject(parentName, 0);
                //设置数组长度
                currentStrucVar_str.SetNumElements(length);
            }

            ////获取当前节点下所有子节点
            //PropertyObject[] propertyObjects = strucVar.GetSubProperties("", 0);
            ////存放节点名称
            //List<string> propertyObjectNames = new List<string>();
            //foreach (PropertyObject propertyObject in propertyObjects)
            //{
            //    propertyObjectNames.Add(propertyObject.Name);
            //}


            foreach (var joitem in jb)
            {
                //含"#"的为多国语言参数,不显示在TS中
                if (joitem.Key.Contains("#"))
                {
                    continue;
                }

                ////判断TS中是否含有当前节点，不含有的话TS删除
                //if (!propertyObjectNames.Contains(joitem.Key))
                //{
                //    strucVar.DeleteSubProperty(joitem.Key, 0);
                //}

                //判断节点为类
                if (joitem.Value.GetType() == typeof(JObject))
                {
                    string key = joitem.Key;
                    //判断是否可以创建层级
                    strucVar.ValidateNewSubPropertyName(key, false, out bool canValid_Input);
                    if (canValid_Input)
                    {
                        //TS中创建新的子级
                        strucVar.NewSubProperty(key, PropertyValueTypes.PropValType_Container, false, "", 0);
                    }
                    PropertyObject currentStrucVar = strucVar.GetPropertyObject(key, 0);
                    JObject jbitem = JsonConvert.DeserializeObject(joitem.Value.ToString()) as JObject;
                    WriteDataToTS(jbitem, currentStrucVar, joitem.Key);


                }
                //判断节点为属性
                else if (joitem.Value.GetType() == typeof(JValue))
                {
                    string key = joitem.Key;
                    if (!isArray)
                    {
                        strucVar.Parent.ValidateNewSubPropertyName(key, false, out bool canValid);
                        if (canValid)
                        {
                            switch (joitem.Value.Type)
                            {
                                case JTokenType.String:
                                    /* 暂未未找到直接修改节点数据类型的方法，如可以修改数据类型，则直接第3步，再修改数据类型  */
                                    //1、获取父节点
                                    PropertyObject propertyObject_str = strucVar.Parent;
                                    //2、删除指定的containter子节点
                                    propertyObject_str.DeleteSubProperty(parentName, 0);
                                    //3、重新创建指定数据类型子节点
                                    propertyObject_str.NewSubProperty(parentName, propertyValueTypes, false, "", 0);
                                    break;
                                case JTokenType.Boolean:
                                    PropertyObject propertyObject_bool = strucVar.Parent;
                                    propertyObject_bool.DeleteSubProperty(parentName, 0);
                                    propertyObject_bool.NewSubProperty(parentName, propertyValueTypes, false, "", 0);
                                    break;
                                case JTokenType.Integer:
                                    PropertyObject propertyObject_int = strucVar.Parent;
                                    propertyObject_int.DeleteSubProperty(parentName, 0);
                                    propertyObject_int.NewSubProperty(parentName, propertyValueTypes, false, "", 0);
                                    break;
                                case JTokenType.Float:
                                    PropertyObject propertyObject_float = strucVar.Parent;
                                    propertyObject_float.DeleteSubProperty(parentName, 0);
                                    propertyObject_float.NewSubProperty(parentName, propertyValueTypes, false, "", 0);
                                    break;
                            }
                        }
                    }
                }
            }
        }


        private static void CreateBaseNode(JObject jb, string parentName, ref CustomerNode customerNode)
        {
            //判断是不是数组，有多个元素
            bool isArray = IsArray(jb, out int length, out PropertyValueTypes propertyValueTypes);
            foreach (var joitem in jb)
            {
                //含"#"的为多国语言参数,不显示在TS中
                if (joitem.Key.Contains("#"))
                {
                    continue;
                }

                //判断节点为类
                if (joitem.Value.GetType() == typeof(JObject))
                {
                    string key = joitem.Key;
                    CustomerNode childNode = new CustomerNode(key);
                    JObject jbitem = JsonConvert.DeserializeObject(joitem.Value.ToString()) as JObject;
                    CreateBaseNode(jbitem, joitem.Key, ref childNode);

                    customerNode.Add(childNode);
                }
                //判断节点为属性
                else if (joitem.Value.GetType() == typeof(JValue))
                {
                    string key = joitem.Key;
                    if (!isArray)
                    {
                        CustomerNode childNode = new CustomerNode(key);
                        childNode.ObjectID = (int)joitem.Value;
                        customerNode.Add(childNode);
                    }
                    else
                    {
                        CustomerNode childNode = new CustomerNode(key);
                        childNode.ObjectID = (int)joitem.Value;
                        customerNode.Add(childNode);
                    }
                }
            }
        }












        /// <summary>
        /// 递归将数据写入TS
        /// </summary>
        /// <param name="jb">读取的本地json文件</param>
        /// <param name="strucVar">TestStand的变量节点</param>
        /// <param name="parentName">父节点名称</param>
        /// <param name="customerNode">自定义的节点类，和TS中变量对应，用于检索</param>
        private static void Recursion1(JObject jb, PropertyObject strucVar, string parentName, ref CustomerNode customerNode)
        {
            //判断是不是数组，有多个元素
            bool isArray = IsArray(jb, out int length, out PropertyValueTypes propertyValueTypes);

            if (isArray)
            {
                //父节点
                PropertyObject parentPropertyObject = strucVar.Parent;
                //删除子节点
                parentPropertyObject.DeleteSubProperty(parentName, 0);
                //重新添加数组类型的子节点
                parentPropertyObject.NewSubProperty(parentName, propertyValueTypes, true, "", 0);
                PropertyObject currentStrucVar_str = parentPropertyObject.GetPropertyObject(parentName, 0);
                //设置数组长度
                currentStrucVar_str.SetNumElements(length);
            }

            foreach (var joitem in jb)
            {
                //含"#"的为多国语言参数,不显示在TS中
                if (joitem.Key.Contains("#"))
                {
                    continue;
                }

                //判断节点为类
                if (joitem.Value.GetType() == typeof(JObject))
                {
                    string key = joitem.Key;
                    //判断是否可以创建层级
                    strucVar.ValidateNewSubPropertyName(key, false, out bool canValid_Input);
                    if (canValid_Input)
                    {
                        //TS中创建新的子级
                        strucVar.NewSubProperty(key, PropertyValueTypes.PropValType_Container, false, "", 0);
                    }
                    PropertyObject currentStrucVar = strucVar.GetPropertyObject(key, 0);
                    CustomerNode childNode = new CustomerNode(key);
                    JObject jbitem = JsonConvert.DeserializeObject(joitem.Value.ToString()) as JObject;
                    Recursion1(jbitem, currentStrucVar, joitem.Key, ref childNode);

                    customerNode.Add(childNode);
                }
                //判断节点为属性
                else if (joitem.Value.GetType() == typeof(JValue))
                {
                    string key = joitem.Key;
                    if (!isArray)
                    {
                        strucVar.Parent.ValidateNewSubPropertyName(key, false, out bool canValid);
                        if (canValid)
                        {
                            switch (joitem.Value.Type)
                            {
                                case JTokenType.String:
                                    /* 暂未未找到直接修改节点数据类型的方法，如可以修改数据类型，则直接第3步，再修改数据类型  */
                                    //1、获取父节点
                                    PropertyObject propertyObject_str = strucVar.Parent;
                                    //2、删除指定的containter子节点
                                    propertyObject_str.DeleteSubProperty(parentName, 0);
                                    //3、重新创建指定数据类型子节点
                                    propertyObject_str.NewSubProperty(parentName, propertyValueTypes, false, "", 0);
                                    break;
                                case JTokenType.Boolean:
                                    PropertyObject propertyObject_bool = strucVar.Parent;
                                    propertyObject_bool.DeleteSubProperty(parentName, 0);
                                    propertyObject_bool.NewSubProperty(parentName, propertyValueTypes, false, "", 0);
                                    break;
                                case JTokenType.Integer:
                                    PropertyObject propertyObject_int = strucVar.Parent;
                                    propertyObject_int.DeleteSubProperty(parentName, 0);
                                    propertyObject_int.NewSubProperty(parentName, propertyValueTypes, false, "", 0);
                                    break;
                                case JTokenType.Float:
                                    PropertyObject propertyObject_float = strucVar.Parent;
                                    propertyObject_float.DeleteSubProperty(parentName, 0);
                                    propertyObject_float.NewSubProperty(parentName, propertyValueTypes, false, "", 0);
                                    break;
                            }
                            CustomerNode childNode = new CustomerNode(key);
                            childNode.ObjectID = (int)joitem.Value;
                            customerNode.Add(childNode);
                        }
                    }
                    else
                    {
                        CustomerNode childNode = new CustomerNode(key);
                        childNode.ObjectID = (int)joitem.Value;
                        customerNode.Add(childNode);
                    }
                }
            }
        }

        /// <summary>
        /// 判断是否为数组
        /// </summary>
        /// <param name="jb">输入json数据</param>
        /// <param name="arrayLenth">返回数组长度</param>
        /// <param name="propertyValueTypes">返回属性数据类型</param>
        /// <returns></returns>
        private static bool IsArray(JObject jb, out int arrayLenth, out PropertyValueTypes propertyValueTypes)
        {
            bool isArray = false;
            arrayLenth = 0;
            propertyValueTypes = PropertyValueTypes.PropValType_Container;

            foreach (var joitem in jb)
            {
                //剔除注释部分数据
                if (joitem.Key.Contains("#"))
                {
                    continue;
                }
                if (joitem.Value.GetType() == typeof(JValue))
                {
                    //字典绑定的key值是ObjectID ，通过objectID获取对应的ConfigurationItem对象
                    ConfigurationItem configurationItem = modbusOptions.DicModubus[(int)joitem.Value];

                    switch (configurationItem.DataType)
                    {
                        case "Integer":
                            //从ConfigurationItem中获取数据类型
                            propertyValueTypes = PropertyValueTypes.PropValType_Number;
                            break;
                        case "Double":
                            propertyValueTypes = PropertyValueTypes.PropValType_Number;
                            break;
                        case "String":
                            propertyValueTypes = PropertyValueTypes.PropValType_String;
                            break;
                        case "Boolean":
                            propertyValueTypes = PropertyValueTypes.PropValType_Boolean;
                            break;

                            ////下面为Neurocheck信号，兼容设置
                            //case "(Process)Free Signal":
                            //    propertyValueTypes = PropertyValueTypes.PropValType_Boolean;
                            //    break;
                            //case "(Process)Signal":
                            //    propertyValueTypes = PropertyValueTypes.PropValType_Boolean;
                            //    break;
                            //case "(Process)Identifier":
                            //    propertyValueTypes = PropertyValueTypes.PropValType_Number;
                            //    break;
                    }

                    arrayLenth++;
                }
            }
            if (arrayLenth > 1)
            {
                isArray = true;
            }
            return isArray;
        }

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="strucVar_customer">数据写入TS的位置</param>
        /// <param name="congigFilePath">配置参数所在路径</param>
        /// <param name="ErrorCode">错误码</param>
        /// <param name="ErrorText">错误信息</param>
        /// <param name="showErrorMessage">是否发生错误信息，true发生，false未发生</param>
        /// <param name="modelOptionsPath">TS配置参数所在路径</param>
        /// <returns></returns>
        public static bool Init(PropertyObject strucVar_customer, string congigFilePath, out int ErrorCode, out string ErrorText, out bool showErrorMessage, string modelOptionsPath = @"V:\Station\OMS\Profile\TestStand\TestStandModelModelOptions.ini")
        {
            try
            {
                ErrorCode = 0;
                ErrorText = "NoError";
                showErrorMessage = false;

                //尝试关闭所有已开启的通讯
                foreach (var key in thread_Modbus_Dic.Keys)
                {
                    object commuNC = thread_Modbus_Dic[key].CommuNC;
                    thread_Modbus_Dic[key].MethodInfo_Dispose.Invoke(commuNC, null);
                }
                //重新创建通讯
                ModbusState = CreatSlave(modelOptionsPath, out ErrorCode, out ErrorText);
                if (ErrorCode == -1)
                {
                    return ModbusState;
                }
                //读json数据
                LoadObjectToTS(strucVar_customer, congigFilePath, out ErrorCode, out ErrorText);
                return ModbusState;
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = ex.Message + ex.StackTrace;
                showErrorMessage = true;
                return false;
            }
        }

        /// <summary>
        /// 读取Modbus信号到数组的指定元素中
        /// </summary>
        /// <param name="pinName">输入的节点</param>
        /// <param name="arrayIndex">元素索引号</param>
        /// <param name="ErrorCode">错误码</param>
        /// <param name="ErrorText">错误信息</param>
        /// <param name="showErrorMessage">是否发生错误信息，true发生，false未发生</param>
        /// <returns></returns>
        public static bool ReadFromSinglePin(PropertyObject pinName, int arrayIndex, out int ErrorCode, out string ErrorText, out bool showErrorMessage)
        {
            try
            {
                ErrorCode = 0;
                ErrorText = "NoError";
                showErrorMessage = false;
                //Name获取Container的名称
                string fullPath = pinName.Name;
                //获取节点所在TS完整路径
                GetFullPath(pinName, ref fullPath, out int socketIndex);
                CustomerNode customerNode = null;
                //根据TS完整路径，找到对应的customerNode对象，配置信息存储在customerNode中
                GetCustomerNode(fullPath, baseCustomerNode, ref customerNode);
                if (customerNode == null)
                {
                    ErrorCode = -1;
                    ErrorText = "请检查输入节点是否有误";
                    showErrorMessage = true;
                    return false;
                }
                //读取数据
                ReadSingleData(arrayIndex, customerNode, pinName, socketIndex);
                return true;
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = ex.Message + ex.StackTrace;
                showErrorMessage = true;
                return false;
            }
        }
        /// <summary>
        /// 将数组的指定元素中数据写入Modbus
        /// </summary>
        /// <param name="pinName">输入的节点</param>
        /// <param name="arrayIndex">元素索引号</param>
        /// <param name="ErrorCode">错误码</param>
        /// <param name="ErrorText">错误信息</param>
        /// <param name="showErrorMessage">是否发生错误信息，true发生，false未发生</param>
        /// <returns></returns>
        public static bool WriteToSinglePin(PropertyObject pinName, int arrayIndex, out int ErrorCode, out string ErrorText, out bool showErrorMessage)
        {
            try
            {
                ErrorCode = 0;
                ErrorText = "NoError";
                showErrorMessage = false;

                string fullPath = pinName.Name;
                //获取节点所在TS完整路径
                GetFullPath(pinName, ref fullPath, out int socketIndex);
                CustomerNode customerNode = null;
                //根据TS完整路径，找到对应的customerNode对象，配置信息存储在customerNode中
                GetCustomerNode(fullPath, baseCustomerNode, ref customerNode);
                if (customerNode == null)
                {
                    ErrorCode = -1;
                    ErrorText = "请检查输入节点是否有误";
                    showErrorMessage = true;
                    return false;
                }
                //发送数据
                WriteSingleData(arrayIndex, customerNode, pinName, socketIndex);
                return true;
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = ex.Message + ex.StackTrace;
                showErrorMessage = true;
                return false;
            }
        }

        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="pinName">输入的节点</param>
        /// <param name="ErrorCode">错误码</param>
        /// <param name="ErrorText">错误信息</param>
        /// <param name="showErrorMessage">是否发生错误信息，true发生，false未发生</param>
        /// <returns></returns>
        public static bool ReadFromPin(PropertyObject pinName, out int ErrorCode, out string ErrorText, out bool showErrorMessage)
        {
            try
            {
                ErrorCode = 0;
                ErrorText = "NoError";
                showErrorMessage = false;

                string fullPath = pinName.Name;
                //获取节点所在TS完整路径
                GetFullPath(pinName, ref fullPath, out int socketIndex);
                CustomerNode customerNode = null;
                //根据TS完整路径，找到对应的customerNode对象，配置信息存储在customerNode中
                GetCustomerNode(fullPath, baseCustomerNode, ref customerNode);
                if (customerNode == null)
                {
                    ErrorCode = -1;
                    ErrorText = "请检查输入节点是否有误";
                    showErrorMessage = true;
                    return false;
                }
                //从modbus读取节点及子节点所有数据
                ReadAllData(customerNode, pinName, socketIndex);
                return true;
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = ex.Message + ex.StackTrace;
                showErrorMessage = true;
                return false;
            }
        }

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="pinName">输入的节点</param>
        /// <param name="ErrorCode">错误码</param>
        /// <param name="ErrorText">错误信息</param>
        /// <param name="showErrorMessage">是否发生错误信息，true发生，false未发生</param>
        /// <returns></returns>
        public static bool WriteToPin(PropertyObject pinName, out int ErrorCode, out string ErrorText, out bool showErrorMessage)
        {
            try
            {
                ErrorCode = 0;
                ErrorText = "NoError";
                showErrorMessage = false;

                string fullPath = pinName.Name;

                GetFullPath(pinName, ref fullPath, out int socketIndex);
                CustomerNode customerNode = null;

                GetCustomerNode(fullPath, baseCustomerNode, ref customerNode);

                if (customerNode == null)
                {
                    ErrorCode = -1;
                    ErrorText = "请检查输入节点是否有误";
                    showErrorMessage = true;
                    return false;
                }
                //将节点及子节点下所有数据都写入Modbus
                WriteAllData(customerNode, pinName, socketIndex);
                return true;
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = ex.Message + ex.StackTrace;
                showErrorMessage = true;
                return false;
            }

        }

        /// <summary>
        /// 读取数组单个元素数据
        /// </summary>
        /// <param name="arrayIndex"></param>
        /// <param name="CustomerNode"></param>
        /// <param name="propertyObject"></param>
        /// <param name="socketIndex"></param>
        private static void ReadSingleData(int arrayIndex, CustomerNode CustomerNode, PropertyObject propertyObject, int socketIndex)
        {
            for (int i = 0; i < CustomerNode.Nodes.Count; i++)
            {
                if (i == arrayIndex)
                {
                    //从CustomerNode下获取完成路径 
                    string path = CustomerNode.Nodes[i].FullPath;
                    PropertyObject data = propertyObject;
                    ConfigurationItem configurationItem = modbusOptions.DicModubus[CustomerNode.Nodes[i].ObjectID];
                    int modbusAddress = configurationItem.ModbusAddress;
                    string modbusType = configurationItem.ModubusType;
                    string dataType = configurationItem.DataType;

                    PropertyObjectType t = data.Type;
                    string str = t.DisplayString.ToLower();

                    //读取数据         PLC从0开始，代码方法从1开始,在界面填写PLC地址时和PLC保持一致从0开始
                    object[] par = new object[] { modbusAddress + 1, modbusType, dataType };
                    object commuNC = thread_Modbus_Dic[socketIndex].CommuNC;
                    object value = thread_Modbus_Dic[socketIndex].MethodInfo_ReadFromPin.Invoke(commuNC, new object[] { par });

                    if (str.Contains("array"))
                    {
                        //判断数组元素索引   path路径如"base\\Input\\Test\\Object2"     Object不止1个则为数组，2为索引，如果只有一个则只有Obeject0
                        int index = Convert.ToInt32(path.Substring(path.Length - 1));

                        if (dataType.ToLower().Contains("int"))
                        {
                            data.SetValNumberByOffset(index, 0, Convert.ToInt32(value));

                        }
                        else if (dataType.ToLower().Contains("double"))
                        {

                            data.SetValNumberByOffset(index, 0, Convert.ToDouble(value));
                        }
                        else if (dataType.ToLower().Contains("string"))
                        {

                            data.SetValStringByOffset(index, 0, value.ToString());
                        }
                        else if (dataType.ToLower().Contains("boolean"))
                        {

                            data.SetValBooleanByOffset(index, 0, Convert.ToBoolean(value));
                        }
                    }
                    else
                    {
                        if (dataType.ToLower().Contains("int"))
                        {
                            data.SetValNumber("", 0, Convert.ToDouble(value));
                        }
                        else if (dataType.ToLower().Contains("double"))
                        {
                            data.SetValNumber("", 0, Convert.ToDouble(value));
                        }
                        else if (dataType.ToLower().Contains("string"))
                        {
                            data.SetValString("", 0, value.ToString());
                        }
                        else if (dataType.ToLower().Contains("boolean"))
                        {
                            data.SetValBoolean("", 0, Convert.ToBoolean(value));
                        }
                    }


                }
            }
        }
        /// <summary>
        /// 读取节点及子节点所有数据
        /// </summary>
        /// <param name="CustomerNode">自定义节点类</param>
        /// <param name="propertyObject">TS节点</param>
        /// <param name="socketIndex">线程号</param>
        private static void ReadAllData(CustomerNode CustomerNode, PropertyObject propertyObject, int socketIndex)
        {
            //判断是否为存储数据元素，object不为-1
            if (CustomerNode.ObjectID != -1)
            {
                string path = CustomerNode.FullPath;
                PropertyObject data = propertyObject;
                //   GetPropertyObject(path, propertyObject, ref data);

                ConfigurationItem configurationItem = modbusOptions.DicModubus[CustomerNode.ObjectID];
                int modbusAddress = configurationItem.ModbusAddress;
                string modbusType = configurationItem.ModubusType;
                string dataType = configurationItem.DataType;

                PropertyObjectType t = data.Type;
                string str = t.DisplayString.ToLower();

                //读取数据         PLC从0开始，代码方法从1开始,在界面填写PLC地址时和PLC保持一致从0开始
                ////  data = commuNC.ReadFromPin(modbusAddress + 1, modbusType, DataType);
                object[] par = new object[] { modbusAddress + 1, modbusType, dataType };
                object commuNC = thread_Modbus_Dic[socketIndex].CommuNC;
                object value = thread_Modbus_Dic[socketIndex].MethodInfo_ReadFromPin.Invoke(commuNC, new object[] { par });

                if (str.Contains("array"))
                {
                    //判断数组元素索引
                    int index = Convert.ToInt32(path.Substring(path.Length - 1));

                    if (dataType.ToLower().Contains("int"))
                    {
                        data.SetValNumberByOffset(index, 0, Convert.ToInt32(value));

                    }
                    else if (dataType.ToLower().Contains("double"))
                    {

                        data.SetValNumberByOffset(index, 0, Convert.ToDouble(value));
                    }
                    else if (dataType.ToLower().Contains("string"))
                    {

                        data.SetValStringByOffset(index, 0, value.ToString());
                    }
                    else if (dataType.ToLower().Contains("boolean"))
                    {

                        data.SetValBooleanByOffset(index, 0, Convert.ToBoolean(value));
                    }
                }
                else
                {
                    if (dataType.ToLower().Contains("int"))
                    {
                        data.SetValNumber("", 0, Convert.ToDouble(value));
                    }
                    else if (dataType.ToLower().Contains("double"))
                    {
                        data.SetValNumber("", 0, Convert.ToDouble(value));
                    }
                    else if (dataType.ToLower().Contains("string"))
                    {
                        data.SetValString("", 0, value.ToString());
                    }
                    else if (dataType.ToLower().Contains("boolean"))
                    {
                        data.SetValBoolean("", 0, Convert.ToBoolean(value));
                    }
                }
            }

            foreach (CustomerNode item in CustomerNode.Nodes)
            {
                ReadAllData(item, propertyObject, socketIndex);
            }

        }

        /// <summary>
        /// 获取当前的customerNode
        /// </summary>
        /// <param name="fullPath">层级路径</param>
        /// <param name="baseCustomerNode">customerNode的根节点</param>
        /// <param name="outCustomerNode">根据路径找到的customerNode节点</param>
        private static void GetCustomerNode(string fullPath, CustomerNode baseCustomerNode, ref CustomerNode outCustomerNode)
        {
            if (baseCustomerNode.FullPath == fullPath)
            {
                outCustomerNode = baseCustomerNode;
            }
            else
            {
                foreach (var item in baseCustomerNode.Nodes)
                {
                    GetCustomerNode(fullPath, item, ref outCustomerNode);
                }
            }
        }
        /// <summary>
        /// 将单个元素数据写入Modbus
        /// </summary>
        /// <param name="arrayIndex">数组索引</param>
        /// <param name="CustomerNode">自定义节点类</param>
        /// <param name="propertyObject">TS节点</param>
        /// <param name="socketIndex">线程号</param>
        private static void WriteSingleData(int arrayIndex, CustomerNode CustomerNode, PropertyObject propertyObject, int socketIndex)
        {
            for (int i = 0; i < CustomerNode.Nodes.Count; i++)
            {
                if (i == arrayIndex)
                {
                    string path = CustomerNode.Nodes[i].FullPath;
                    PropertyObject data = propertyObject;

                    ConfigurationItem configurationItem = modbusOptions.DicModubus[CustomerNode.Nodes[i].ObjectID];
                    int modbusAddress = configurationItem.ModbusAddress;
                    string modbusType = configurationItem.ModubusType;
                    string dataType = configurationItem.DataType;

                    PropertyObjectType t = data.Type;
                    string str = t.DisplayString.ToLower();
                    object sendValue = null;

                    //判断数组元素索引
                    int index = Convert.ToInt32(path.Substring(path.Length - 1));
                    //判断数组数据类型
                    if (str.Contains("number"))
                    {
                        if (dataType.ToLower().Contains("int"))
                        {
                            //判断是否为数组
                            var v = str.Contains("array") ? data.GetValNumberByOffset(index, 0) : data.GetValNumber("", 0);
                            sendValue = (int)v;
                        }
                        else if (dataType.ToLower().Contains("double"))
                        {
                            var v = str.Contains("array") ? data.GetValNumberByOffset(index, 0) : data.GetValNumber("", 0);
                            sendValue = (double)v;
                        }
                    }
                    else if (str.Contains("string"))
                    {
                        sendValue = str.Contains("array") ? data.GetValStringByOffset(index, 0) : data.GetValString("", 0);
                    }
                    else if (str.Contains("boolean"))
                    {
                        sendValue = str.Contains("array") ? data.GetValBooleanByOffset(index, 0) : data.GetValBoolean("", 0);
                    }

                    object[] par = new object[] { modbusAddress + 1, modbusType, sendValue, dataType };
                    object commuNC = thread_Modbus_Dic[socketIndex].CommuNC;
                    thread_Modbus_Dic[socketIndex].MethodInfo_WriteToPin.Invoke(commuNC, new object[] { par });
                }
            }

        }

        /// <summary>
        /// 将节点及子节点所有数据写入Modbus
        /// </summary>
        /// <param name="CustomerNode">自定义节点类</param>
        /// <param name="propertyObject">TS节点</param>
        /// <param name="socketIndex">线程号</param>
        private static void WriteAllData(CustomerNode CustomerNode, PropertyObject propertyObject, int socketIndex)
        {
            //判断是否为存储数据元素，object不为-1
            if (CustomerNode.ObjectID != -1)
            {
                string path = CustomerNode.FullPath;
                PropertyObject data = propertyObject;

                //  GetPropertyObject(path, propertyObject, ref data);

                ConfigurationItem configurationItem = modbusOptions.DicModubus[CustomerNode.ObjectID];
                int modbusAddress = configurationItem.ModbusAddress;
                string modbusType = configurationItem.ModubusType;
                string dataType = configurationItem.DataType;

                PropertyObjectType t = data.Type;
                string str = t.DisplayString.ToLower();
                object sendValue = null;


                //判断数组元素索引
                int index = Convert.ToInt32(path.Substring(path.Length - 1));
                //判断数组数据类型
                if (str.Contains("number"))
                {
                    if (dataType.ToLower().Contains("int"))
                    {
                        //判断是否为数组
                        var v = str.Contains("array") ? data.GetValNumberByOffset(index, 0) : data.GetValNumber("", 0);
                        sendValue = (int)v;
                        // int sendValue = value.GetValNumberByOffset(0, 0);
                    }
                    else if (dataType.ToLower().Contains("double"))
                    {
                        var v = str.Contains("array") ? data.GetValNumberByOffset(index, 0) : data.GetValNumber("", 0);
                        sendValue = (double)v;
                    }
                }
                else if (str.Contains("string"))
                {
                    sendValue = str.Contains("array") ? data.GetValStringByOffset(index, 0) : data.GetValString("", 0);
                }
                else if (str.Contains("boolean"))
                {
                    sendValue = str.Contains("array") ? data.GetValBooleanByOffset(index, 0) : data.GetValBoolean("", 0);
                }

                object[] par = new object[] { modbusAddress + 1, modbusType, sendValue, dataType };
                object commuNC = thread_Modbus_Dic[socketIndex].CommuNC;
                thread_Modbus_Dic[socketIndex].MethodInfo_WriteToPin.Invoke(commuNC, new object[] { par });
            }

            foreach (CustomerNode item in CustomerNode.Nodes)
            {
                WriteAllData(item, propertyObject, socketIndex);
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="fullPath">完整路径</param>
        /// <param name="propertyObject">TS节点</param>
        /// <param name="outpropertyObject">返回TS节点</param>
        private static void GetPropertyObject(string fullPath, PropertyObject propertyObject, ref PropertyObject outpropertyObject)
        {

            PropertyObject[] propertyObjects = propertyObject.GetSubProperties("", 0);
            //length为0  没有子节点，则此节点存放数据  存放数据的可能为某种数据类型，也可能是某种数据类型的数组
            if (propertyObjects.Length == 0)
            {
                string outPath = propertyObject.Name;
                //通过输入的propertyObject对象，拼接出完整路径返回，并返回线程号     
                GetFullPath(propertyObject, ref outPath, out int socketIndex);

                string dis = propertyObject.Type.DisplayString;
                //判断是不是数组
                if (dis.Contains("Array"))
                {
                    int num = propertyObject.GetNumElements();
                    for (int i = 0; i < num; i++)
                    {
                        //若为数组，判断是数组中否有元素路径和传入的一致
                        string tempPath = outPath + "\\" + "Object" + i;
                        if (tempPath == fullPath)
                        {
                            outpropertyObject = propertyObject;
                        }
                    }
                }
                else
                {
                    //不是数组，则判断Object0路径是否和传入的一致
                    outPath = outPath + "\\" + "Object0";

                    if (outPath == fullPath)
                    {
                        outpropertyObject = propertyObject;
                    }
                }
            }
            else
            {
                foreach (var item in propertyObjects)
                {
                    GetPropertyObject(fullPath, item, ref outpropertyObject);
                }
            }
        }

        /// <summary>
        ///  通过TS的PropertyObject拼接出完整路径，并返回所在线程号
        /// </summary>
        /// <param name="pinName"></param>
        /// <param name="fullPath"></param>
        /// <param name="socketIndex"></param>
        private static void GetFullPath(PropertyObject pinName, ref string fullPath, out int socketIndex)
        {
            socketIndex = -1;
            if (pinName.Parent != null)
            {
                if ("input" != pinName.Name && "output" != pinName.Name)
                {
                    fullPath = pinName.Parent.Name + "\\" + fullPath;
                    GetFullPath(pinName.Parent, ref fullPath, out socketIndex);
                }
                else if (pinName.Parent.Name.Contains("Socket"))
                {
                    fullPath = "base\\" + fullPath;
                    socketIndex = int.Parse(pinName.Parent.Name.Substring(6, 1));
                }
            }
        }


        /// <summary>
        /// 创建通讯slave
        /// </summary>
        /// <param name="modelOptionsPath"></param>
        /// <param name="ErrorCode"></param>
        /// <param name="ErrorText"></param>
        /// <returns></returns>
        private static bool CreatSlave(string modelOptionsPath, out int ErrorCode, out string ErrorText)
        {
            try
            {
                Type type = typeof(ModbusCallerTS);
                //获取dll的当前路径
                string path = type.Assembly.Location;
                //dll所在文件夹
                string currentDirPath = Path.GetDirectoryName(path);


                thread_Modbus_Dic.Clear();
                ErrorCode = 0;
                ErrorText = "";
                // 1.反射加载ICommu_NC.dll
                Assembly assembly = Assembly.LoadFrom(currentDirPath + "\\ICommu_NC.dll");
                Type[] mtypes = assembly.GetTypes();

                // 2.获取ICommu_NC.dll下的"CommunNCManager"管理类（管理类控制加载dll）
                var mtype = mtypes.Where(t => t.Name == "CommunNCManager").ToList();
                if (mtype.Count != 1)
                {
                    MessageBox.Show("只能有一个程序集");
                    return false;
                }

                // 3.创建CommunNCManager类实例
                communNCManager = Activator.CreateInstance(mtype[0]);

                // 4.获取CommunNCManager类下的"LoadDll"方法
                MethodInfo methodInfo_Loaddll = mtype[0].GetMethod("LoadDll");

                //获取线程数量
                try
                {
                    threadCount = GetThreadCount(modelOptionsPath);

                }
                catch (Exception ex)
                {
                    ErrorCode = -1;
                    ErrorText = "获取线程数量失败！" + ex.Message;
                    return false;
                }

                for (int i = 0; i < threadCount; i++)
                {
                    //创建存储通讯信息的对象  
                    Thread_Modbus thread_Modbus = new Thread_Modbus();

                    // 5.读取ini配置文件，加载配置中指定的dll，返回ICommuNC接口实例，其中保存加载dll的实例

                    object retIcommuNC = methodInfo_Loaddll.Invoke(communNCManager, new object[] { currentDirPath , currentDirPath + "\\CL_VisionModbus.ini", "Socket" + i.ToString(), "InterfaceDLL" });

                    if (retIcommuNC != null)
                    {
                        thread_Modbus.CommuNC = retIcommuNC;
                    }
                    else
                    {
                        ErrorCode = -1;
                        ErrorText = $"第{i}号线程参数读取失败，请确实参数是否配置！";
                        return false;
                    }
                    // 6.获取"ICommuNC"接口下的"CreatConnect"方法
                    MethodInfo methodInfo_CreatConnect = null;
                    var itype = mtypes.Where(t => t.IsInterface).ToList();

                    // 7.创建连接
                    methodInfo_CreatConnect = itype[0].GetMethod("CreatConnect");
                    bool connected = (bool)methodInfo_CreatConnect.Invoke(thread_Modbus.CommuNC, null);

                    if (!connected)
                    {
                        ErrorCode = -1;
                        ErrorText = $"第{i}号线程创建连接失败！";
                        return false;
                    }
                    // 8.获取"ICommuNC"接口下的"ReadFromPin"方法和"WriteToPin"方法
                    thread_Modbus.MethodInfo_ReadFromPin = itype[0].GetMethod("ReadFromPin");
                    thread_Modbus.MethodInfo_WriteToPin = itype[0].GetMethod("WriteToPin");
                    thread_Modbus.MethodInfo_Dispose = itype[0].GetMethod("Dispose");

                    /*===创建成功的会被加入字典，创建失败的不会加入，即使返回false，thread_Modbus_Dic可能也不为空，初始化时遍历释放===*/
                    thread_Modbus_Dic.Add(i, thread_Modbus);
                }

                return true;

            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = ex.Message + ex.StackTrace;
                return false;
            }
        }

        private static T JSONToEntity<T>(string jsonStr) where T : class
        {
            T t = default(T);
            try
            {
                t = (T)JsonConvert.DeserializeObject(jsonStr, typeof(T));
            }
            catch (Exception)
            {

                t = default(T);
            }
            return t;
        }

        private static int GetThreadCount(string filePath)
        {
            // 使用FileIniDataParser实例解析或保存ini文件。
            var parser = new FileIniDataParser();

            // 加载INI文件，读取实例中包含的数据，并解析该数据
            IniData data = parser.ReadFile(filePath);
            int threadCount = int.Parse(data["ModelOptions"]["NumTestSockets"]);

            return threadCount;
        }

    }


    public class Thread_Modbus
    {
        private object _commuNC;
        private object _communNCManager;
        private MethodInfo _methodInfo_ReadFromPin;
        private MethodInfo _methodInfo_WriteToPin;
        private MethodInfo _methodInfo_Dispose;

        public object CommuNC { get => _commuNC; set => _commuNC = value; }
        public object CommunNCManager { get => _communNCManager; set => _communNCManager = value; }
        public MethodInfo MethodInfo_ReadFromPin { get => _methodInfo_ReadFromPin; set => _methodInfo_ReadFromPin = value; }
        public MethodInfo MethodInfo_WriteToPin { get => _methodInfo_WriteToPin; set => _methodInfo_WriteToPin = value; }
        public MethodInfo MethodInfo_Dispose { get => _methodInfo_Dispose; set => _methodInfo_Dispose = value; }
    }
}
